package com.util.gis;

import java.math.BigDecimal;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;

/**
 * @Description: TODO
 * @Author: hechaobo
 * @Date: 2024/4/18
 **/
public class GisUtil {
    public static final double PI = 3.1415926535897932384626433832795;

    /**
     * 经纬度转墨卡托
     *
     * @param lon
     * @param lat
     * @return
     */
    public static double[] meridianToMercator(double lon, double lat) {
        double x = lon * 20037508.342 / 180;
        double y = Math.log(Math.tan((90 + lat) * Math.PI / 360)) / (Math.PI / 180);
        y = y * 20037508.342 / 180;
        return new double[]{x, y};
    }

    /**
     * 获取经纬度网格编码
     *
     * @param x
     * @param y
     * @param distance
     * @return
     */
    public static long getCodeByMeridian(double x, double y, int distance) {
        double[] doubles = meridianToMercator(x, y);
        return getCode(doubles[0],doubles[1],distance);
    }

    /**
     * 获取墨卡托网格编码
     *
     * @param x
     * @param y
     * @param distance
     * @return
     */
    public static long getCode(double x, double y, int distance) {
        long a = (long) x / distance * distance;
        long b = (long) y / distance * distance;
        return a * 100000000 + b;
    }

    /**
     * 获取附近墨卡托网格编码
     *
     * @param x
     * @param y
     * @param distance
     * @return
     */
    public static List<Long> getCodeNear(double x, double y, int distance) {
        long[] xx = new long[]{(long) (x - distance), (long) x, (long) (x + distance)};
        long[] yy = new long[]{(long) (y - distance), (long) y, (long) (y + distance)};
        List<Long> longs = new LinkedList<>();
        for (long i : xx) {
            for (long j : yy) {
                i = i / distance * distance;
                j = j / distance * distance;
                longs.add(i * 100000000 + j);
            }
        }
        return longs;
    }
    /**
     * 获取附近网格编码
     */
    public static List<Long> getCodeNearMeridian(double lon, double lat, int distance) {
        double[] doubles = meridianToMercator(lon, lat);
        return getCodeNear(doubles[0], doubles[1], distance);
    }


    /**
     * 计算经纬度距离
     *
     * @param longitude1
     * @param latitude1
     * @param longitude2
     * @param latitude2
     * @return
     */
    public static double calculateDistance(double longitude1, double latitude1, double longitude2, double latitude2) {
        if (longitude1 == 0 || latitude1 == 0 || latitude2 == 0 || longitude2 == 0) {
            return -1.0;
        }
        longitude1 *= 0.01745329251994329;
        latitude1 *= 0.01745329251994329;
        longitude2 *= 0.01745329251994329;
        latitude2 *= 0.01745329251994329;
        double var1 = Math.sin(longitude1);
        double var2 = Math.sin(latitude1);
        double var3 = Math.cos(longitude1);
        double var4 = Math.cos(latitude1);
        double var5 = Math.sin(longitude2);
        double var6 = Math.sin(latitude2);
        double var7 = Math.cos(longitude2);
        double var8 = Math.cos(latitude2);
        double[] var10 = new double[3];
        double[] var20 = new double[3];
        var10[0] = var4 * var3;
        var10[1] = var4 * var1;
        var10[2] = var2;
        var20[0] = var8 * var7;
        var20[1] = var8 * var5;
        var20[2] = var6;

        double d = Math.asin(Math.sqrt((var10[0] - var20[0]) * (var10[0] - var20[0]) + (var10[1] - var20[1]) * (var10[1] - var20[1]) + (var10[2] - var20[2]) * (var10[2] - var20[2])) / 2.0) * 1.27420015798544E7;
        BigDecimal bd = new BigDecimal(d);
        bd = bd.setScale(2, BigDecimal.ROUND_HALF_UP);
        return bd.doubleValue();
    }



    /**
     * WGS-84转GCJ坐标
     * @param wgsLat
     * @param wgsLon
     * @return
     */
    public static Map<String, Double> gcj_encrypt(Double wgsLat, Double wgsLon) {
        Map<String,Double> model = new HashMap<String,Double>();
        Map<String, Double> d = delta(wgsLat, wgsLon);

        model.put("lat", wgsLat + d.get("lat"));
        model.put("lon", wgsLon + d.get("lon"));

        return model;
    }

    /**
     * GCJ坐标转WGS-84坐标
     * @param gcjLat
     * @param gcjLon
     * @return
     */
    public static Map<String, Double> gcj_decrypt(Double gcjLat, Double gcjLon) {
        Map<String, Double> model = new HashMap<String, Double>();
        Map<String, Double> d = delta(gcjLat, gcjLon);

        model.put("lat", gcjLat - (Double) d.get("lat"));
        model.put("lon", gcjLon - (Double) d.get("lon"));

        return model;
    }

    public static final double x_pi = PI * 3000.0 / 180.0;
    /**
     * GCJ-02坐标转百度BD-09坐标
     * @param gcjLat
     * @param gcjLon
     * @return
     */
    public static Map<String, Double> bd_encrypt(Double gcjLat, Double gcjLon) {
        Map<String, Double> model = new HashMap<String, Double>();

        Double x = gcjLon, y = gcjLat;
        Double z = Math.sqrt(x * x + y * y) + 0.00002 * Math.sin(y * x_pi);
        Double theta = Math.atan2(y, x) + 0.000003 * Math.cos(x * x_pi);
        Double bdLon = z * Math.cos(theta) + 0.0065;
        Double bdLat = z * Math.sin(theta) + 0.006;

        model.put("lat", bdLat);
        model.put("lon", bdLon);

        return model;
    }

    /**
     * 百度BD-09坐标转GCJ-02坐标
     * @param bdLat
     * @param bdLon
     * @return
     */
    public static Map<String, Double> bd_decrypt(Double bdLat, Double bdLon) {
        Map<String, Double> model = new HashMap<String, Double>();

        Double x = bdLon - 0.0065, y = bdLat - 0.006;
        Double z = Math.sqrt(x * x + y * y) - 0.00002 * Math.sin(y * x_pi);
        Double theta = Math.atan2(y, x) - 0.000003 * Math.cos(x * x_pi);
        Double gcjLon = z * Math.cos(theta);
        Double gcjLat = z * Math.sin(theta);

        model.put("lat", gcjLat);
        model.put("lon", gcjLon);

        return model;
    }

    public static Double[] wgs84_bd(Double lon, Double lat){
        Map<String, Double> map = gcj_encrypt(lat, lon);
        Map<String, Double> map1 = bd_encrypt(map.get("lat"), map.get("lon"));
        return new Double[]{map1.get("lon"), map1.get("lat")};
    }

    public static Double[] bd_wgs84(Double lon, Double lat){
        Map<String, Double> map = bd_decrypt(lat, lon);
        Map<String, Double> map1 = gcj_decrypt(map.get("lat"), map.get("lon"));
        return new Double[]{map1.get("lon"), map1.get("lat")};
    }



    private static Map<String, Double> delta(Double lat, Double lon) {
        Double a = 6378245.0; //  a: 卫星椭球坐标投影到平面地图坐标系的投影因子。
        Double ee = 0.00669342162296594323; //  ee: 椭球的偏心率。
        Double dLat = transformLat(lon - 105.0, lat - 35.0);
        Double dLon = transformLon(lon - 105.0, lat - 35.0);
        Double radLat = lat / 180.0 * PI;
        Double magic = Math.sin(radLat);
        magic = 1 - ee * magic * magic;
        Double sqrtMagic = Math.sqrt(magic);
        dLat = (dLat * 180.0) / ((a * (1 - ee)) / (magic * sqrtMagic) * PI);
        dLon = (dLon * 180.0) / (a / sqrtMagic * Math.cos(radLat) * PI);

        Map<String,Double> model = new HashMap<String,Double>();

        model.put("lat", dLat);
        model.put("lon", dLon);

        return model;
    }
    private static Double transformLat(Double x, Double y) {
        Double ret = -100.0 + 2.0 * x + 3.0 * y + 0.2 * y * y + 0.1 * x * y + 0.2 * Math.sqrt(Math.abs(x));
        ret += (20.0 * Math.sin(6.0 * x * PI) + 20.0 * Math.sin(2.0 * x * PI)) * 2.0 / 3.0;
        ret += (20.0 * Math.sin(y * PI) + 40.0 * Math.sin(y / 3.0 * PI)) * 2.0 / 3.0;
        ret += (160.0 * Math.sin(y / 12.0 * PI) + 320 * Math.sin(y * PI / 30.0)) * 2.0 / 3.0;

        return ret;
    }
    private static Double transformLon(Double x, Double y) {
        Double ret = 300.0 + x + 2.0 * y + 0.1 * x * x + 0.1 * x * y + 0.1 * Math.sqrt(Math.abs(x));
        ret += (20.0 * Math.sin(6.0 * x * PI) + 20.0 * Math.sin(2.0 * x * PI)) * 2.0 / 3.0;
        ret += (20.0 * Math.sin(x * PI) + 40.0 * Math.sin(x / 3.0 * PI)) * 2.0 / 3.0;
        ret += (150.0 * Math.sin(x / 12.0 * PI) + 300.0 * Math.sin(x / 30.0 * PI)) * 2.0 / 3.0;

        return ret;
    }

}
